package cn.chiship.sdk.core.util;

import cn.chiship.sdk.core.exception.custom.BusinessException;
import cn.chiship.sdk.core.exception.custom.SystemErrorException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @author lijian
 */
public final class ClassUtil {

	private static final Logger LOGGER = LoggerFactory.getLogger(ClassUtil.class);

	/**
	 * 获取类加载器
	 */
	public static ClassLoader getClassLoader() {
		return Thread.currentThread().getContextClassLoader();
	}

	/**
	 * 加载类
	 */
	public static Class<?> loadClass(String className, boolean isInitialized) {
		Class<?> cls;
		try {
			cls = Class.forName(className, isInitialized, getClassLoader());
		}
		catch (ClassNotFoundException e) {
			throw new SystemErrorException(e);
		}
		return cls;
	}

	/**
	 * 加载类（默认将初始化类）
	 */
	public static Class<?> loadClass(String className) {
		return loadClass(className, true);
	}

	/**
	 * 获取指定包名下的所有类
	 */
	public static List<Map<String, Object>> getClassList(String packageName) {
		Set<Class<?>> classSet = new HashSet<>();
		try {
			Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".", "/"));
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				if (url != null) {
					String protocol = url.getProtocol();
					if ("file".equals(protocol)) {
						String packagePath = url.getPath().replace("%20", " ");
						addClass(classSet, packagePath, packageName);
					}
					else if ("jar".equals(protocol)) {
						JarURLConnection jarUrlConnection = (JarURLConnection) url.openConnection();
						if (jarUrlConnection != null) {
							JarFile jarFile = jarUrlConnection.getJarFile();
							if (jarFile != null) {
								Enumeration<JarEntry> jarEntries = jarFile.entries();
								while (jarEntries.hasMoreElements()) {
									JarEntry jarEntry = jarEntries.nextElement();
									String jarEntryName = jarEntry.getName();
									if (jarEntryName.endsWith(".class")) {
										String className = jarEntryName.substring(0, jarEntryName.lastIndexOf("."))
												.replace("/", ".");
										doAddClass(classSet, className);
									}
								}
							}
						}
					}
				}
			}
		}
		catch (Exception e) {
			throw new SystemErrorException(e);
		}

		List<Map<String, Object>> result = new ArrayList<>();
		for (Class str : classSet) {
			Map<String, Object> map = new HashMap<>(2);
			map.put("simpleName", StringUtil.toLowerCaseFirstOne(str.getSimpleName()));
			map.put("canonicalName", str.getCanonicalName());
			map.put("methods", getClassMethods(str.getCanonicalName()));
			result.add(map);
		}

		return result;
	}

	public static Map<String, Boolean> getClassMethods(String className) {
		try {
			Map<String, Boolean> result = new HashMap<>(2);
			Method[] methods = Class.forName(className).getDeclaredMethods();
			for (Method method : methods) {
				result.put(method.getName(), method.getParameterCount() > 0);
			}
			return result;
		}
		catch (ClassNotFoundException e) {
			LOGGER.error("发生异常", e);
			throw new BusinessException("类名不正确");
		}
	}

	private static void addClass(Set<Class<?>> classSet, String packagePath, String packageName) {
		File[] files = new File(packagePath).listFiles(new FileFilter() {
			@Override
			public boolean accept(File file) {
				return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
			}
		});
		for (File file : files) {
			String fileName = file.getName();
			if (file.isFile()) {
				String className = fileName.substring(0, fileName.lastIndexOf("."));
				if (StringUtil.isNotEmpty(packageName)) {
					className = packageName + "." + className;
				}
				doAddClass(classSet, className);
			}
			else {
				String subPackagePath = fileName;
				if (StringUtil.isNotEmpty(packagePath)) {
					subPackagePath = packagePath + "/" + subPackagePath;
				}
				String subPackageName = fileName;
				if (StringUtil.isNotEmpty(packageName)) {
					subPackageName = packageName + "." + subPackageName;
				}
				addClass(classSet, subPackagePath, subPackageName);
			}
		}
	}

	private static void doAddClass(Set<Class<?>> classSet, String className) {
		Class<?> cls = loadClass(className, false);
		classSet.add(cls);
	}

	/**
	 * 属性赋值
	 * @param o 对象
	 * @param fieldName 属性名称
	 * @param fieldValue 属性值
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException
	 */
	public static void setFieldValue(Object o, String fieldName, Object fieldValue) {
		try {
			Class<?> clazz = o.getClass();
			Field createdBy = clazz.getDeclaredField(fieldName);
			createdBy.setAccessible(true);
			createdBy.set(o, fieldValue);
		}
		catch (Exception e) {
		}
	}

	public static void main(String[] args) {
		PrintUtil.console(getClassList("cn.chiship.sdk.core.util.html"));
	}

}
